package main

import (
	"fmt"
	"math/rand"
	"sort"
	"strings"
	"time"
	"unicode"
)

func main() {
	fmt.Println("复合数据类型 ")
	fmt.Println("数组必须指定类型和长度，长度是类型的一部分")
	var a1 [3]bool
	var a2 [4]bool
	fmt.Printf("a1:%T a2:%T", a1, a2)
	fmt.Println("数组初始化")
	a1 = [3]bool{true, true, true}
	a3 := [...]int{0, 1, 2, 3, 4, 5, 99, 100, 199, 221}
	a4 := [5]int{0: 1, 4: 9}
	fmt.Println(a1)
	fmt.Println(a3)
	fmt.Println(a4)
	fmt.Println("数组支持关系运算符，值类型，[n]*T指针数组 *[n]T 数组指针")
	var a5 int
	for i := 0; i < len(a3); i++ {
		a5 += a3[i]
	}
	fmt.Println(a5)
	for k, v := range a3 {
		fmt.Printf("%d : %d", k, v)
	}
	fmt.Println("切片 相同类型的可变长度的  引用类型")
	var a6 = []int{1, 2, 3}
	var a7 = []string{"奥巴马", "猪八戒", "孙悟空"}
	fmt.Println(a6 == nil)
	fmt.Printf("len(n):%d cap(n):%d", len(a7), cap(a7))
	fmt.Println("由数组得到切片,基于左包含右不包含")
	a8 := a3[0:4]
	fmt.Println(a8)
	a9 := a3[:4]
	a10 := a3[4:]
	a11 := a3[:]
	fmt.Println(a9, a10, a11)
	fmt.Printf("len(n):%d cap(n):%d\n", len(a9), cap(a9))
	fmt.Printf("len(n):%d cap(n):%d\n", len(a10), cap(a10)) //从切片位置到最后就是切片的容量
	a12 := a10[:]
	fmt.Printf("len(n):%d cap(n):%d\n", len(a12), cap(a12))
	a3[6] = 2345 //修改了底层的数组
	fmt.Println(a12)
	fmt.Println("切片相当于框了一片连续的内存，1，引用类型2，不能直接比较")
	fmt.Println("一个nil值的切片没有底层数组，长度和容量都是0。但是长度和容量为0的切片不都是nil")
	fmt.Println("判断切片是不是空，用len()==0")
	s1 := make([]int, 5, 10)
	fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
	s2 := make([]int, 0, 10)
	fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d\n", s2, len(s2), cap(s2))
	//切片的赋值
	s3 := []int{1, 2, 3}
	s4 := s3
	fmt.Println(s3, s4)
	s3[0] = 9000
	fmt.Println(s3, s4)
	//遍历   索引for遍历  range遍历
	fmt.Println("snippets=>go 设置快捷键")
	fmt.Println(`首先判断，如果新申请容量（cap）大于2倍的旧容量（old.cap），最终容量（newcap）就是新申请的容量（cap）。
	否则判断，如果旧切片的长度小于1024，则最终容量(newcap)就是旧容量(old.cap)的两倍，即（newcap=doublecap），
	否则判断，如果旧切片长度大于等于1024，则最终容量（newcap）从旧容量（old.cap）开始循环增加原来的1/4，即（newcap=old.cap,for {newcap += newcap/4}）直到最终容量（newcap）大于等于新申请的容量(cap)，即（newcap >= cap）
	如果最终容量（cap）计算值溢出，则最终容量（cap）就是新申请容量（cap）。
	需要注意的是，切片扩容还会根据切片中元素的类型不同而做不同的处理，比如int和string类型的处理方式就不一样。`)

	s5 := []string{"北京", "上海", "广州"}
	fmt.Printf("s5=%v len(s5)=%d cap(s5)=%d\n", s5, len(s5), cap(s5))
	s5 = append(s5, "山西")
	fmt.Printf("s5=%v len(s5)=%d cap(s5)=%d\n", s5, len(s5), cap(s5))
	s6 := []string{"北京1", "上海1", "广州1"}
	s5 = append(s5, s6...) //...表示拆开
	fmt.Printf("s5=%v len(s5)=%d cap(s5)=%d\n", s5, len(s5), cap(s5))
	fmt.Println("copy")
	s8 := s5
	s9 := make([]string, 12, 12)
	copy(s9, s8)
	fmt.Println(s5, s8, s9)
	s5[0] = "www"
	fmt.Println(s5, s8, s9)
	//切片删除索引为1的
	s5 = append(s5[:1], s5[2:]...)
	fmt.Println(s5)
	fmt.Println(cap(s5))
	//数组切片
	fmt.Println("这里不太好理解")
	s10 := [...]int{1, 3, 8, 99, 88} //数组
	s11 := s10[:]                    //切片
	fmt.Printf("s11=%v len(s11)=%d cap(s11)=%d\n", s11, len(s11), cap(s11))
	//切片不保存具体的值
	//切片对应一个底层数组
	//底层数组都是占用一块连续的内存
	fmt.Printf("%p\n", &s11[0])
	s11 = append(s11[:1], s11[2:]...) //修改底层数组
	fmt.Printf("%p\n", &s11[0])
	fmt.Printf("s11=%v len(s11)=%d cap(s11)=%d\n", s11, len(s11), cap(s11))
	fmt.Println(s10)

	var s12 = make([]int, 5, 10)
	for i := 0; i < 10; i++ {
		s12 = append(s12, i)
	}
	fmt.Println(s12)
	var s13 = [...]int{1, 5, 2, 8, 7, 20, 13}
	sort.Ints(s13[:]) //数组转为切片
	fmt.Println(s13)
	fmt.Println("go不存在指针操作，vscode不支持go module  &取地址，*根据地址取值")

	z1 := 22
	z2 := &z1
	fmt.Printf("%T\n", z2)
	fmt.Println("int型指针")
	fmt.Println(*z2)

	var z3 *int //nil
	fmt.Println(z3)
	var z4 = new(int) //内存分配，指针声明
	fmt.Println(z4)
	fmt.Println(*z4)
	*z4 = 10
	fmt.Println(*z4)
	fmt.Println(`make也是用于内存分配的，区别于new，
	它只用于slice、map以及chan的内存创建，
	而且它返回的类型就是这三个类型本身，而不是他们的指针类型，
	因为这三种类型就是引用类型，所以就没有必要返回他们的指针了。`)
	fmt.Println(`二者都是用来做内存分配的。
	make只用于slice、map以及channel的初始化，返回的还是这三个引用类型本身；
	而new用于类型的内存分配，并且内存对应的值为类型零值，返回的是指向类型的指针。`)
	fmt.Println(`Go语言中提供的映射关系容器为map，其内部使用散列表（hash）实现。`)
	fmt.Println(`map是一种无序的基于key-value的数据结构，Go语言中的map是引用类型，必须初始化才能使用。`)
	var m1 map[string]int //没有初始化，没有申请内存
	fmt.Println(m1 == nil)
	m1 = make(map[string]int, 10) //算好容量，避免动态扩容
	m1["郭富城"] = 1000
	m1["任达华"] = 999
	fmt.Println(m1)
	fmt.Println(m1["郭富城"]) //如果不存在，返回对应类型的零值
	value, ok := m1["郭美美"] //返回bool值
	if !ok {
		fmt.Println("无此key")
	} else {
		fmt.Println(value)
	}

	fmt.Println("range遍历map ")
	fmt.Println("看文档 go doc builtin.delete ")
	//删除
	delete(m1, "郭富城")
	fmt.Println(m1)
	delete(m1, "郭富城1") //删除不存在key
	fmt.Println(m1)
	fmt.Println("按照指定顺序遍历map")
	rand.Seed(time.Now().UnixNano()) //初始化随机数种子

	var scoreMap = make(map[string]int, 200)

	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
		value := rand.Intn(100)          //生成0~99的随机整数
		scoreMap[key] = value
	}
	//取出map中的所有key存入切片keys
	var keys = make([]string, 0, 200)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	//对切片进行排序
	sort.Strings(keys)
	//按照排序后的key遍历map
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
	fmt.Println("元素为map类型的切片")
	var m2 = make([]map[int]string, 10, 10)
	m2[0] = make(map[int]string, 1)
	m2[0][10] = "范冰冰"
	fmt.Println(m2)
	fmt.Println("值为切片类型的map")

	var m3 = make(map[string][]int, 10)
	m3["北京"] = []int{1, 3, 5}
	fmt.Println(m3)
	fmt.Println("写一个程序，统计一个字符串中每个单词出现的次数。比如：”how do you do”中how=1 do=2 you=1。")
	fmt.Println("观察下面代码，写出最终的打印结果。")
	type Map map[string][]int
	m := make(Map)
	s := []int{1, 2}
	s = append(s, 3)
	fmt.Printf("%+v\n", s)
	m["q1mi"] = s
	s = append(s[:1], s[2:]...)
	fmt.Printf("%+v\n", s)
	fmt.Printf("%+v\n", m["q1mi"])
	f11("wjp", 1, 2, 3, 4, 5)
	fmt.Println("go语言没有默认参数")
	m4 := "12312312424地方还是撒可富"
	var count int
	for _, v := range m4 {
		if unicode.Is(unicode.Han, v) {
			count++
		}
	}
	fmt.Println(count)
	m5 := "how do you do"
	m6 := strings.Split(m5, " ")
	m7 := make(map[string]int, 10)
	for _, v := range m6 {
		if _, ok := m7[v]; !ok {
			m7[v] = 1
		} else {
			m7[v]++
		}
	}
	for key, value := range m7 {
		fmt.Println(key, value)
	}
	//回文判断
	ss := "山西运煤车煤运西山"
	r := make([]rune, 0, len(ss))
	for _, c := range ss {
		r = append(r, c)
	}
	fmt.Println("[]rune", r)
	for i := 0; i < len(r)/2; i++ {
		if r[i] != r[len(r)-1-i] {
			fmt.Println("不是回文")
			return
		}
	}
	fmt.Println("是回文")
}

func f11(x string, y ...int) {
	fmt.Println(x)
	fmt.Println(y)
}
